<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>文件上传测试</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
    <script src="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.js"></script>
    <style>
        ul li:first-line {
            font-weight: bold;
            color: red;
        }
    </style>
</head>
<body>
<div>
    <div>
        <form accept-charset="UTF-8"
              action="/upload/uploadFile"
              method="post"
              enctype="multipart/form-data">
            <label for="normal">一般文件上传</label>
            <input id="normal" name="file" type="file">
            <input type="submit" value="提交">
        </form>
    </div>
    <br/>
    <br/>
    <br/>
    <div>
        <label for="big">大文件上传断点续传</label>
        <input id="big" type="file" onchange="uploadBigFile(event)">
        <div>
            <span>进度:</span><span id="percent"></span>
            <ul id="progress" style="height: 600px;overflow-y: auto"></ul>
        </div>
    </div>
</div>
<script>
    var REQUEST_URL = "http://192.168.14.107:8080";

    let fileUploader = null;

    $(document).ready(() => {
        // 按5MB进行分片
        fileUploader = new FileUploader(5 * 1024 * 1024);
    })

    function uploadBigFile(event) {
        fileUploader.startUpload(event.target.files);
    }

    class FileUploader {
        static ALREADY_UPLOADED = 0;

        /**
         * @param chunkSize 分片大小
         */
        constructor(chunkSize) {
            this.chunkSize = chunkSize;
            this.fileObject = {
                hasError: false
            };
            this.wulObj = {};
            this.$progress = $("#progress");
            this._initGlobalWebUploader();
            this.webUploader = this._newWebUploader();
        }

        startUpload(files) {
            this.interval = setInterval(() => {
                this.$progress[0].scrollTop = this.$progress[0].scrollHeight;
            }, 300);
            this.webUploader.addFiles(files);
        }

        _initGlobalWebUploader() {
            WebUploader.Uploader.register(
                {
                    "before-send-file": "beforeSendFile",
                    "before-send": "beforeSend",
                    "after-send-file": "afterSendFile"
                },
                {
                    beforeSendFile: file => {
                        this.$progress.append(`<li>beforeSendFile 每个文件只会触发一次<br/>file ${Object.entries(file)}</li>`);
                        let deferred = WebUploader.Deferred();
                        // 计算文件MD5
                        this.webUploader
                            .md5File(file)
                            .progress(percent => {
                                this.$progress.children(":last-child").html(`<li>文件md5计算进度: ${Math.ceil(percent * 100) + "%"}</li>`);
                            })
                            .then(fileMd5 => {
                                    this.wulObj[file.id] = {};
                                    this.wulObj[file.id].fileMd5 = fileMd5;
                                    this.wulObj[file.id].fileSize = file.size;
                                    this.wulObj[file.id].chunkSize = this.chunkSize;
                                    this.wulObj[file.id].fileName = file.name;
                                    let reqJsonObj = Object.assign({}, this.wulObj[file.id]);
                                    this.$progress.append(`<li>调用checkBigFile检查文件中...</li>`);
                                    $.ajax({
                                        type: "POST",
                                        url: REQUEST_URL+"/upload/checkBigFile",
                                        data: reqJsonObj,
                                        dataType: "json",
                                        success: resJsonObj => {
                                            if (typeof resJsonObj.data === "string") {
                                                this.$progress.append(`<li>文件检查完成,状态:已完成上传,访问链接:${resJsonObj.data}</li>`);
                                                this.fileObject[file.id].url = resJsonObj.data;
                                                deferred.reject(FileUploader.ALREADY_UPLOADED);
                                            } else {
                                                this.$progress.append(`<li>文件检查完成,状态:缺失分片${resJsonObj.data}</li>`);
                                                this.fileObject[file.id].missingChunks = resJsonObj.data;
                                                deferred.resolve();
                                            }
                                        },
                                        error: e => {
                                            deferred.reject(e);
                                        },
                                    });
                                }
                            );
                        return deferred.promise();
                    },
                    beforeSend: block => {
                        this.$progress.append(`<li>beforeSend:block 文件每个分片上传前触发一次<br/>${Object.entries(block)}</li>`);
                        let deferred = WebUploader.Deferred();
                        if (this.fileObject[block.file.id].missingChunks.indexOf(block.chunk) === -1) {
                            this.$progress.append(`<li>服务器已存在该分片${block.chunk},跳过上传</li>`);
                            deferred.reject();
                        }
                        this.webUploader
                            .md5File(block.blob)
                            .then(chunkMd5 => {
                                    this.$progress.append(`<li>完成计算block ${block.chunk} md5:${chunkMd5}</li>`);
                                    this.wulObj[block.file.id][block.chunk] = {};
                                    this.wulObj[block.file.id][block.chunk].formData = {
                                        chunk: block.chunk,
                                        fileMd5: this.wulObj[block.file.id].fileMd5,
                                        chunkMd5: chunkMd5,
                                    };
                                    deferred.resolve();
                                }
                            );
                        return deferred.promise();
                    },
                    afterSendFile: file => {
                        this.$progress.append(`<li>afterSendFile:file 所有分片上传完成,调用mergeBigFile合并文件分片<br/>${Object.entries(file)} </li>`);
                        $.ajax({
                            type: "POST",
                            url: REQUEST_URL+"/upload/mergeBigFile",
                            data: {
                                fileMd5: this.wulObj[file.id].fileMd5,
                                fileName: file.name,
                                chunkSize: this.chunkSize,
                                size: this.wulObj[file.id].fileSize,
                            },
                            success: resJsonObj => {
                                this.$progress.append(`<li>服务器合并文件分片完成,文件链接:${resJsonObj.data}</li>`);
                                this.fileObject[file.id].url = resJsonObj.data;
                                this.wulObj[file.id] = {};
                            }
                        });
                    }
                }
            );
        }

        _newWebUploader() {
            let webUploader = WebUploader.create({
                // 文件上传路径
                server: REQUEST_URL+"/upload/uploadBigFile",
                // 自动上传
                auto: true,
                // 禁止浏览器打开文件
                disableGlobalDnd: true,
                // 分片上传
                chunked: true,
                // 分片大小
                chunkSize: this.chunkSize,
                // 分片上传失败重试次数
                chunkRetry: 1,
                // 图片不做压缩
                compress: false,
                // 提前准备好下一个文件
                prepareNextFile: true,
                // 限制单个文件大小
                fileSingleSizeLimit: 200 * 1024 * 1024,
                fileNumLimit: 5,
                //线程数
                threads: 5,
                // 限制格式
                accept: {
                    title: 'file',
                    extensions: '*',
                    mimeTypes: '*/*'
                }
            });
            webUploader.on(
                "fileQueued",
                file => {
                    this.$progress.append(`<li>fileQueued:file<br/>${Object.entries(file)}</li>`);
                    this.fileObject[file.id] = {};
                    this.fileObject[file.id].name = file.name;
                    this.fileObject[file.id].blobUrl = URL.createObjectURL(file.source.source);
                }
            );
            webUploader.on(
                "uploadBeforeSend",
                (block, headers) => {
                    this.$progress.append(`<li>uploadBeforeSend:block,headers<br/>${Object.entries(block)} ${Object.entries(headers)}</li>`);
                    Object.assign(headers, this.wulObj[block.file.id][block.chunk].formData);
                }
            );
            webUploader.on(
                "uploadProgress",
                (file, percentage) => {
                    this.$progress.append(`<li>uploadProgress:file,percentage<br/>${Object.entries(file)} ${Math.ceil(percentage * 100) + "%"}</li>`);
                    this.fileObject[file.id].percentage = Math.ceil(percentage * 100);
                    $("#percent").text(this.fileObject[file.id].percentage + "%");
                }
            );
            webUploader.on(
                "uploadAccept",
                (obj, res) => {
                    this.$progress.append(`<li>uploadAccept:file,res<br/>${Object.entries(obj.file)} ${Object.entries(res)}</li>`);
                    return res.code === 0;

                }
            );
            webUploader.on(
                "uploadSuccess",
                file => {
                    this.$progress.append(`<li>uploadSuccess:file<br/>${Object.entries(file)}</li>`);
                    setTimeout(() => clearInterval(this.interval), 1000);
                }
            );
            webUploader.on(
                "uploadError",
                (file, res) => {
                    if (res === FileUploader.ALREADY_UPLOADED) {
                        this.fileObject[file.id].percentage = 100;
                        $("#percent").text(this.fileObject[file.id].percentage + "%");
                    } else {
                        this.$progress.append(`<li>uploadError:file<br/>${Object.entries(file)} res:${res}</li>`);
                        this.fileObject[file.id].hasError = true;
                    }
                }
            );
            webUploader.on(
                "uploadComplete",
                file => {
                    this.$progress.append(`<li>uploadComplete:file<br/>${Object.entries(file)} ${file.getStatus()}</li>`);
                    if (this.fileObject[file.id].hasError) {
                        $("#percent").text("上传出错,请重试");
                    }
                    setTimeout(() => clearInterval(this.interval), 1000);
                }
            );
            webUploader.on(
                "error",
                type => {
                    this.$progress.append(`<li>error:type ${type}</li>`);
                    console.error("error:", type);
                }
            );
            return webUploader;
        }
    }
</script>
</body>
</html>
